﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using SharpDX;

namespace DX11Engine.PlugIns
{
    public static class MQOReader
    {
        private static string[] ExtractFromString(string orString, string KeyWord, char separator = ' ')
        {
            if (!orString.Contains(KeyWord)) { return null; }
            string sbst = orString.Substring(orString.IndexOf(KeyWord + "(") + KeyWord.Length + 1);
            return sbst.Substring(0, sbst.IndexOf(")")).Split(separator);
        }

        private static float[] GetValueFloats(string orString, string KeyWord, char separator = ' ')
        {
            if (!orString.Contains(KeyWord)) { return null; }
            string[] itms = ExtractFromString(orString, KeyWord, separator);
            float[] rtrnVal = new float[itms.Length];
            for (int x = 0; x < rtrnVal.Length; x++)
            { rtrnVal[x] = (float)Convert.ToDecimal(itms[x]); }
            return rtrnVal;
        }

        private static float GetValueFloat(string orString, string KeyWord, float defaultVal = 0)
        { 
            string[] rslt = ExtractFromString(orString, KeyWord);
            return (rslt != null && rslt.Length > 0) ? (float)Convert.ToDecimal(rslt[0]) : defaultVal; 
        }

        private static string GetValueString(string orString, string KeyWord, string defaultVal = null)
        { 
            string[] rslt = ExtractFromString(orString, KeyWord);
            return (rslt != null && rslt.Length > 0) ? rslt[0] : defaultVal; 
        }

        public static Dictionary<MeshMaterial, PositionNormalTextured[]> LoadMesh(string FileName, float scale = .01f, bool FlipTriangles = true)
        {
            string[] lins = File.ReadAllLines(FileName);
            if (lins != null)
            {
                MeshMaterial[] MaterialData = null; int CMatIndx = 0;
                Dictionary<int, List<PositionNormalTextured[]>> ObjectData = new Dictionary<int, List<PositionNormalTextured[]>>();

                Vector3[] crntIndexPool = null; int CIndxP = 0;

                string ReadFlag = ""; int CurrentItems = 0;
                for (int cLindx = 0; cLindx < lins.Length; cLindx++)
                {
                    string cLine = lins[cLindx].Trim().ToUpper();
                    switch (ReadFlag)
                    {
                        case "MATERIAL":
                            {
                                if (cLine.Trim() != "}")
                                {
                                    if (MaterialData == null) { MaterialData = new MeshMaterial[CurrentItems]; }
                                    string TxtrNm = cLine.Substring(0, cLine.IndexOf(' ')).Trim().Replace("\"", "");

                                    if (MaterialData[CMatIndx] == null)
                                    {
                                        MaterialData[CMatIndx] = new MeshMaterial(TxtrNm)
                                        {
                                            RGBA = GetValueFloats(cLine, "COL"),
                                            Diffuse = GetValueFloat(cLine, "DIF"),
                                            Ambient = GetValueFloat(cLine, "AMB"),
                                            Emission = GetValueFloat(cLine, "EMI"),
                                            Power = GetValueFloat(cLine, "POWER"),
                                            Specular = GetValueFloat(cLine, "SPC")
                                        };

                                    }

                                    string TextureFileName = GetValueString(cLine, "TEX");
                                    if (!String.IsNullOrEmpty(TextureFileName))
                                    {
                                        MaterialData[CMatIndx].TextLayers = new TextureLayer[] { 
                                            new TextureLayer(TextureFileName.Replace("\"", ""), LayerType.Texture) };
                                    }

                                    CMatIndx++;
                                }
                                else { ReadFlag = ""; CurrentItems = 0; }
                                break;
                            }
                        case "VERTEX":
                            {
                                if (cLine.Trim() != "}" && CIndxP < CurrentItems)
                                {
                                    if (crntIndexPool == null) { crntIndexPool = new Vector3[CurrentItems]; }
                                    string[] triang = cLine.Split(' ');
                                    crntIndexPool[CIndxP] = new Vector3((float)Convert.ToDecimal(triang[0]) * scale,
                                        (float)Convert.ToDecimal(triang[1]) * scale, (float)Convert.ToDecimal(triang[2]) * scale);
                                    CIndxP++;
                                }
                                else { ReadFlag = ""; CIndxP = 0; CurrentItems = 0; }
                                break;
                            }
                        case "FACE":
                            {
                                if (cLine.Trim() != "}")
                                {
                                    float[] polyData = GetValueFloats(cLine, "V");
                                    int matID = Convert.ToInt32(GetValueFloat(cLine, "M"));
                                    float[] txtrData = GetValueFloats(cLine, "UV");

                                    if (!ObjectData.ContainsKey(matID))
                                    { ObjectData.Add(matID, new List<PositionNormalTextured[]>()); }

                                    int FaceVerteces = Convert.ToInt32(cLine.Substring(0, cLine.IndexOf(" ")).Trim());
                                    if (FaceVerteces == 3)
                                    {
                                        PositionNormalTextured[] CrntVertxPool = new PositionNormalTextured[FaceVerteces];
                                        for (int y = 0; y < FaceVerteces; y++)
                                        {
                                            CrntVertxPool[y] = new PositionNormalTextured() { 
                                                Position = crntIndexPool[(int)polyData[y]] ,
                                                Color = new Vector4(MaterialData[matID].RGBA[0],
                                                    MaterialData[matID].RGBA[1],
                                                    MaterialData[matID].RGBA[2],
                                                    MaterialData[matID].RGBA[3])
                                            };
                                            
                                            if (txtrData != null) { CrntVertxPool[y].Tex = new Vector2(txtrData[y * 2], txtrData[(y * 2) + 1]); }
                                        }
                                        JEntityFactory.GenerateTriangleNormalTangentBinormalData(ref CrntVertxPool);
                                        ObjectData[matID].Add(CrntVertxPool);
                                    }
                                    else { throw new Exception("Polygons may only be triangles!"); }
                                }
                                else { ReadFlag = ""; crntIndexPool = null; CurrentItems = 0; }
                                break;
                            }
                        default:
                            {
                                string[] ctokens = cLine.Split(' ');
                                if ((ctokens[0] == "MATERIAL" || ctokens[0] == "FACE" || ctokens[0] == "VERTEX") && 
                                    (ctokens[1] == "{"|| ctokens[2] == "{"))
                                { 
                                    if (!Int32.TryParse(ctokens[1], out CurrentItems)) { CurrentItems = 0; }
                                    ReadFlag = ctokens[0];
                                }
                                if (ctokens[0] == "EOF")
                                {
                                    Dictionary<MeshMaterial, PositionNormalTextured[]> exportValue = 
                                            new Dictionary<MeshMaterial, PositionNormalTextured[]>();
                                    for(int x = 0; x < MaterialData.Length;x++)
                                    {
                                        PositionNormalTextured[] MaterialTriangles = new PositionNormalTextured[ObjectData[x].Count*3];
                                        for (int i = 0; i < ObjectData[x].Count; i++)
                                        {
                                            MaterialTriangles[i*3] = ObjectData[x][i][FlipTriangles ? 2 : 0];
                                            MaterialTriangles[(i*3)+1] = ObjectData[x][i][1];
                                            MaterialTriangles[(i * 3) + 2] = ObjectData[x][i][FlipTriangles ? 0 : 2];
                                        }
                                        exportValue.Add(MaterialData[x],MaterialTriangles);
                                    }
                                    return exportValue;
                                }
                                break;
                            }
                    }
                }
            }
            return null;
        }
    }
}
